﻿// Spun AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

function scalarUpdate(id, value) {
    $(id).innerHTML=value;
}

/* Actions */

function ActionCallback(transport, controller, method) {
    var action="Action '" + method + "' on '" + controller + "'";

    if(transport.responseJSON != null) {
        var Response=transport.responseJSON;
        
        if(Response.Success == false) {
            if(Response.ExceptionSummary != null) {
                alert(action + ' caused server-side exception: ' + Response.ExceptionSummary.Message);
            } else {
                alert(action + ' failed');
            }
        }
    } else {
        if(transport.status != 200) {
            alert(action + ' failed with HTTP status ' + transport.status);
        }
    }
}

function Action(controller, method, argument) {
    var Url= 'action/' + controller + '/' + method + '.action';
    
    if(argument) {
        Url = Url + '?arg=' + Object.toJSON(argument);
    }
    
    new Ajax.Request(Url, { 
        method: 'get', 
        onFailure: function(transport) { ActionCallback(transport, controller, method); },
        onSuccess: function(transport) { ActionCallback(transport, controller, method); }
    });
}

/* binding */

var BindingClient = Class.create({
    initialize: function(ID, updateMethod) {
        this.ID=ID;
        this.updateMethod=updateMethod;
    },
    
    setValue: function(value) {
        this.value=value;
        this.updateMethod(this.ID, value);
    },
    
    invalidate: function() {
        this.value=null;
        this.updateMethod(this.ID, null);
    }
});

var RegistrarClass = Class.create({
    initialize: function() {
        this.BindingMap=$H();
    },
    
    bind: function(ID, path, updateMethod) {
        var BindingMap=this.BindingMap;
    
        if(BindingMap.get(path) == undefined)
            BindingMap.set(path, new Array());
         
        var Bindings=BindingMap.get(path);
        
        for(var i=0; i<Bindings.length; i++) {
            if(Bindings[i].ID == ID)
                return;
        }
        
        /* If we got here, then we've got to add a new binding */
        
        var value=null;
        if(Bindings.length > 0) 
            value=Bindings[0].value;
        
        Bindings.push(new BindingClient(ID, updateMethod));
    }
});

var Registrar=new RegistrarClass();

function register(ID, path, updateMethod) { /* Use this method when binding during page-load. */    
    Registrar.bind(ID, path, updateMethod);
}

function InitBindings() {
    var Request = {BindingPaths: Registrar.BindingMap.keys()};
    
    Action('binding', 'bind', Request);
}

function Bind(ID, path, updateMethod) {/* Use this method when binding after page-load. */
    var OldBindingMap=Registrar.BindingMap.get(path);

    Registrar.bind(ID, path, updateMethod);
    
    if(OldBindingMap == undefined) {
        Action('binding', 'bind', {BindingPaths: [path]});
    }
}

/* sequence binding */

var SequenceBinder = Class.create({
    initialize: function(container) {
        this.Count=0;
        this.container=$(container);
    },
    
    update: function(id, value) {
        if(value > this.Count) {
            for(var i = this.Count; i < value; i++) {
				this.createElement(i);
			}
        } else if(value < this.Count) {
			alert('TODO: implement deletion');
		}
		
		this.Count=value;
    },
    
    createElement: function(position) {}
});

/* Polling */

function ProcessUpdates(ReturnValue) {
    if(ReturnValue == null)
        return;
        
    var Updates=ReturnValue['Updates'];
    
    if(Updates == null)
        return;
        
    for(var i=0; i<Updates.length; i++) {
        var Update=Updates[i];
        
        var BindingClients=Registrar.BindingMap.get(Update.Path);
        
        if(BindingClients != undefined) {
            BindingClients.each(function(BindingClient) {
                if(Update.Valid)
                    BindingClient.setValue(Update.Object);
                else
                    BindingClient.invalidate();
            });
        }
    }
}

function PollingCallback(transport) {
    if(transport.status != 200) {
        alert('Connection to server lost - status ' + transport.status);
    } else {
        var Timeout=10000;
        
        var Response=transport.responseJSON;
        
        if(Response != null) {
            if(Response.Success == false) {
                if(Response.ExceptionSummary != null) {
                    alert('Server-side exception caused by polling: ' + Response.ExceptionSummary.Message);
                } else {
                    alert('Polling failed');
                }
            } else {
                Timeout=100;
                ProcessUpdates(Response.ReturnValue);
            }
        } 
           
        setTimeout('Poll();', Timeout);
    } 
} 

function Poll() {
    new Ajax.Request('polling', { 
        method: 'get', 
        onFailure: PollingCallback,
        onSuccess: PollingCallback
    });
}

function StartSpun() {
    Poll();
}

/* Controls */

var Control = Class.create({
    initialize: function(element, path) {
        this.element=$(element);
        this.path=path;
        
        Bind(element, path, function(id, value) { element.setValue(value); });
        
        element.onchange = function() { this.SendUpdate(); }.bind(this);
    },
    
    SendUpdate: function() {
        var Update = {
            BindingPath: this.path,
            Value: this.GetValue()
        };
        
        Action('binding', 'update', Update);
    },
    
    GetValue: function() { return null; }
});

var TextField = Class.create(Control, {
    GetValue: function() { return this.element.getValue(); }
});

var CheckBox = Class.create(Control, {
    GetValue: function() { return (this.element.getValue() == 'on'); }
});